﻿using Abp;
using Abp.Auditing;
using Abp.Runtime.Security;
using Abp.Runtime.Session;
using Abp.Web.Security.AntiForgery;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Mt.Site.Application.Sessions.Dto;
using Mt.Site.Application.UiCustomization;
using Mt.Site.Core;
using Mt.Site.Core.Authentication.TwoFactor;
using Mt.Site.Core.Authorization.Delegation;
using Mt.Site.Core.Authorization.Users;
using Mt.Site.Core.Base.Authorization;
using Mt.Site.Core.Base.Authorization.Users;
using Mt.Site.Core.Base.Identity;
using Mt.Site.Core.Security;
using Mt.Site.Core.Synchronization;
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace Mt.Site.Application.Sessions
{
    public class SessionAppService : MtSiteAppServiceBase, ISessionAppService
    {
        private readonly IUiThemeCustomizerFactory _uiThemeCustomizerFactory;

        private readonly IUserDelegationConfiguration _userDelegationConfiguration;

        private readonly IOnlineUserManager _onlineUserManager;
        private readonly IRSACryptoService _rSACryptoService;
        private readonly IAbpAntiForgeryManager _abpAntiForgeryManager;
        private readonly SignInManager _signInManager;
        private readonly UserClaimsPrincipalFactory _principalFactory;

        private readonly ISynchronizationHelper _synchronizationHelper;

        public SessionAppService(
            IUiThemeCustomizerFactory uiThemeCustomizerFactory,
            IUserDelegationConfiguration userDelegationConfiguration, 
            IOnlineUserManager onlineUserManager,
            IRSACryptoService rSACryptoService,
            IAbpAntiForgeryManager abpAntiForgeryManager,
            SignInManager signInManager,
            UserClaimsPrincipalFactory principalFactory,
            ISynchronizationHelper synchronizationHelper)
        {
            _uiThemeCustomizerFactory = uiThemeCustomizerFactory;
            _userDelegationConfiguration = userDelegationConfiguration;
            _onlineUserManager = onlineUserManager;
            _rSACryptoService = rSACryptoService;
            _abpAntiForgeryManager = abpAntiForgeryManager;
            _signInManager = signInManager;
            _principalFactory = principalFactory;

            _synchronizationHelper = synchronizationHelper;
        }

        [DisableAuditing]
        public async Task<GetCurrentLoginInformationsOutput> GetCurrentLoginInformations()
        {
            var output = new GetCurrentLoginInformationsOutput
            {
                Application = new ApplicationInfoDto
                {
                    Version = AppVersionHelper.Version,
                    ReleaseDate = AppVersionHelper.ReleaseDate,
                    Features = new Dictionary<string, bool>(),
                    Currency = MtSiteConsts2.Currency,
                    CurrencySign = MtSiteConsts2.CurrencySign,
                    AllowTenantsToChangeEmailSettings = MtSiteConsts2.AllowTenantsToChangeEmailSettings,
                    UserDelegationIsEnabled = _userDelegationConfiguration.IsEnabled,
                    TwoFactorCodeExpireSeconds = TwoFactorCodeCacheItem.DefaultSlidingExpireTime.TotalSeconds
                }
            };

            var uiCustomizer = await _uiThemeCustomizerFactory.GetCurrentUiCustomizer();
            output.Theme = uiCustomizer.GetUiSettings();

            if (AbpSession.TenantId.HasValue)
            {
                output.Tenant = ObjectMapper
                    .Map<TenantLoginInfoDto>(await TenantManager
                        .Tenants
                        .Include(t => t.Edition)
                        .FirstAsync(t => t.Id == AbpSession.GetTenantId()));
            }

            if (AbpSession.ImpersonatorTenantId.HasValue)
            {
                output.ImpersonatorTenant = ObjectMapper
                    .Map<TenantLoginInfoDto>(await TenantManager
                        .Tenants
                        .Include(t => t.Edition)
                        .FirstAsync(t => t.Id == AbpSession.ImpersonatorTenantId));
            }

            if (AbpSession.UserId.HasValue)
            {
                output.User = ObjectMapper.Map<UserLoginInfoDto>(await GetCurrentUserAsync());
            }

            if (AbpSession.ImpersonatorUserId.HasValue)
            {
                output.ImpersonatorUser = ObjectMapper.Map<UserLoginInfoDto>(await GetImpersonatorUserAsync());
            }

            if (output.Tenant == null)
            {
                return output;
            }

            if (output.Tenant.Edition != null)
            {

            }

            if (AbpSession.UserId.HasValue)
            {
                output.MotherSiteInfo = _synchronizationHelper.GetMotherSiteInfo(AbpSession.TenantId.Value, AbpSession.CultureName());
            }

            output.Tenant.CreationTimeString = output.Tenant.CreationTime.ToString("d");

            return output;
        }


        [DisableAuditing]
        [IgnoreAntiforgeryToken]
        public PingResult Ping()
        {
            var result = new PingResult();
            //todo;可以成为signalr的补充，在有必要的情况下可以用来做消息通信（有sessionid作为标识，只要服务端做一个缓存就够了）
            var userId = AbpSession.UserId;
            if(userId == null)
            {
                result.Msg = "0";

                ////重新登陆及刷新token
                //var userData = await _onlineUserManager.GetCurrentUser();
                //if (userData != null)
                //{
                //    var user = await UserManager.GetUserOrNullAsync(new UserIdentifier(userData.TenantId, userData.UserId));
                //    if (user != null) 
                //    {
                //        var identity = (ClaimsIdentity)(await _principalFactory.CreateAsync(user)).Identity;
                //        if (userData.ImpersonatorTenantId.HasValue)
                //        {
                //            identity.AddClaim(new Claim(AbpClaimTypes.ImpersonatorTenantId, userData.ImpersonatorTenantId.Value.ToString()));
                //        }

                //        if (userData.ImpersonatorUserId.HasValue)
                //        {
                //            identity.AddClaim(new Claim(AbpClaimTypes.ImpersonatorUserId, userData.ImpersonatorUserId.Value.ToString()));
                //        }
                //        await _signInManager.SignInAsync(identity, false);
                        
                //        result.Msg = "2";
                //    }
                //}
                //else
                //{
                //    result.Msg = "0";
                //}
            }
            else
            {
                //var b = await _onlineUserManager.RefreshExpireTimeAsync(userId.Value, AbpSession.TenantId, AbpSession.ImpersonatorTenantId, AbpSession.ImpersonatorUserId);
                //result.Msg = b.ToString();

                result.Msg = "1";
            }

            return result;
        }

        [DisableAuditing]
        [IgnoreAntiforgeryToken]
        public void GetNewToken()
        {
            _abpAntiForgeryManager.SetCookie(_onlineUserManager.HttpContextAccessor.HttpContext);
        }

        public async Task LockScreenAsync()
        {
            await _onlineUserManager.LockAsync(AbpSession.ToUserIdentifier());
        }

        public async Task<UnlockOutput> UnLockAsync(UnlockInput input)
        {
            if (AbpSession.UserId.HasValue)
            {
                input.Password = _rSACryptoService.Decrypt(input.Password);
                if (AbpSession.ImpersonatorUserId.HasValue)
                {
                    using (CurrentUnitOfWork.SetTenantId(AbpSession.ImpersonatorTenantId))
                    {
                        var user = await UserManager.GetUserByIdAsync(AbpSession.ImpersonatorUserId.Value);
                        if (!await UserManager.CheckPasswordAsync(user, input.Password))
                        {
                            return new UnlockOutput
                            {
                                Message = "密码有误",
                                IsSuccess = false
                            };
                        }
                    }
                }
                else
                {
                    var user = await UserManager.GetUserByIdAsync(AbpSession.UserId.Value);
                    if (!await UserManager.CheckPasswordAsync(user, input.Password))
                    {
                        return new UnlockOutput
                        {
                            Message = "密码有误",
                            IsSuccess = false
                        };
                    }
                }

                await _onlineUserManager.UnlockAsync(AbpSession.ToUserIdentifier());

                return new UnlockOutput
                {
                    IsSuccess = true
                };
            }

            return new UnlockOutput
            {
                Message = "用户状态已过期，请转到登录页登录",
                IsSuccess = false
            };
        }

        public async Task<UpdateUserSignInTokenOutput> UpdateUserSignInToken()
        {
            if (AbpSession.UserId <= 0)
            {
                throw new Exception(L("ThereIsNoLoggedInUser"));
            }

            var user = await UserManager.GetUserAsync(AbpSession.ToUserIdentifier());
            user.SetSignInToken();
            return new UpdateUserSignInTokenOutput
            {
                SignInToken = user.SignInToken,
                EncodedUserId = Convert.ToBase64String(Encoding.UTF8.GetBytes(user.Id.ToString())),
                EncodedTenantId = user.TenantId.HasValue
                    ? Convert.ToBase64String(Encoding.UTF8.GetBytes(user.TenantId.Value.ToString()))
                    : ""
            };
        }

        protected virtual async Task<User> GetImpersonatorUserAsync()
        {
            using (CurrentUnitOfWork.SetTenantId(AbpSession.ImpersonatorTenantId))
            {
                var user = await UserManager.FindByIdAsync(AbpSession.ImpersonatorUserId.ToString());
                if (user == null)
                {
                    throw new Exception("User not found!");
                }

                return user;
            }
        }
    }
}